package com.xsjiong.jaript;

import com.xsjiong.jaript.lexer.JLexer;
import com.xsjiong.jaript.lexer.JLexerException;
import com.xsjiong.jaript.lexer.JSymbols;
import com.xsjiong.jaript.utils.JUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;
import com.xsjiong.jaript.utils.*;
import java.lang.reflect.*;

public class JCompiler {
	private JContext cx;
	private JStringPool P = new JStringPool();
	private JLexer L;
	private String[] Variables = null;
	private Class<?>[] VC = null;
	private ByteArrayOutputStream Q;
	private byte S;
	private int VariableTop;
	private boolean SV;

	public JCompiler(JContext cx) {
		this.cx = cx;
		this.Variables = new String[cx.getVariableTableSize()];
		VC = new Class<?>[cx.getVariableTableSize()];
	}

	void setVariableTableSize(int size) {
		this.Variables = null;
		this.VC = null;
		System.gc();
		this.Variables = new String[size];
		this.VC = new Class<?>[size];
	}
	
	private Class<?> getClassForType(String n) {
		Class<?> cl = JReflector.getClassSafely(cx.getImportHelper().expandClassName(n));
		if (cl!=null) return cl;
		switch (n) {
			case "int":return Integer.TYPE;
			case "byte":return Byte.TYPE;
			case "short":return Short.TYPE;
			case "long":return Long.TYPE;
			case "float":return Float.TYPE;
			case "double":return Double.TYPE;
			case "boolean":return Boolean.TYPE;
			case "char":return Character.TYPE;
		}
		return null;
	}

	public byte[] compileCode(String s) throws JLexerException, JCompilerException {
		return compileCode(new JLexer(s));
	}

	private void SE(String des) throws JCompilerException {
		throw new JCompilerException(L.getIndex(), des);
	}

	private void UN(byte type) throws JCompilerException {
		throw new JCompilerException(L.getIndex(), "Unexpected " + JSymbols._GetSymbolName(type));
	}

	private void WriteShort(short i) throws IOException {
		Q.write(JUtils.ShortToBytes(i));
	}

	private void WriteInt(int i) throws IOException {
		Q.write(JUtils.IntToBytes(i));
	}

	private void WriteLong(long i) throws IOException {
		Q.write(JUtils.LongToBytes(i));
	}

	private void WriteByte(byte i) throws IOException {
		Q.write(i);
	}

	private void WriteFloat(float i) throws IOException {
		Q.write(JUtils.FloatToBytes(i));
	}

	private void WriteDouble(double i) throws IOException {
		Q.write(JUtils.DoubleToByts(i));
	}

	private void WriteBoolean(boolean i) throws IOException {
		Q.write(i ? 0 : 1);
	}

	private void WriteChar(char i) throws IOException {
		WriteShort((short) i); // 方法重用，代码冗杂，勿喷
	}

	private void WriteString(String s) throws IOException {
		Q.write(JUtils.IntToBytes(P.putString(s)));
	}

	private boolean isSeparator() {
		return S == JSymbols.Comma || S == JSymbols.Semicolon;
	}

	private int findIndex(String d) {
		for (int i = 0; i <= VariableTop; i++)
			if (Variables[i].equals(d)) return i;
		return -1;
	}
	
	private static class JMember {
		Object V;
		public JMember(Object a) {
			this.V=a;
		}
		public boolean isVariable() {
			return V instanceof Object;
		}
		public boolean isClass() {
			return V instanceof Class;
		}
	}

	private Object Factor() throws JLexerException, JCompilerException {
		S = L.ReadSymbol();
		StringBuffer tmp = new StringBuffer();
		Object obj = null;
		String last = null;
		boolean li = false;
		switch (S) {
			case JSymbols.Integer:
				li=false;
				return Integer.parseInt(L.getToken());
			case JSymbols.String:
				li=false;
				return L.getString();
			case JSymbols.Identifier:
				/*if (obj == null) {
					if (tmp.length()==0) {
						tmp.append(L.getToken());
						int ind = findIndex(tmp.toString());
						if (ind != -1) {
							obj = new Variable(ind);
							break;
						}
					} else tmp.append('.').append(L.getToken());
					obj = JReflector.getClassSafely(tmp.toString());
				} else {
					Class<?> cl = obj.getClass();
					if (last != null) {
						if (cl instanceof Class)
								cl = (Class) obj;
						else if (obj instanceof Field)
							cl = ((Field) obj).getType();
						obj = JReflector.getMember(cl, last);
						if (obj == null)
							throw new RuntimeException("找不到 " + obj + " 下的成员 "+last);
					}
					last = L.getToken();
					li=true;
				}*/
				String s = L.getToken();
				Object ret;
				if (L.ReadSymbol()=='.')
					ret = new 
				break;
			default:
				if (li) {
					L.gotoLast();
					
				}
				UN(S);
				return null;
		}
		return null;
	}

	private static class Variable {
		public int Index;

		public Variable(int i) {
			Index = i;
		}
	}

	private void WriteVariable(int ind) throws IOException {
		if (SV)
			WriteShort((short) ind);
		else
			WriteInt(ind);
	}

	private boolean isPrimitiveObject(Object obj) {
		if (obj == null) return false;
		return obj instanceof Number || obj instanceof Character || obj instanceof Boolean;
	}

	private int ConstString(int ind, String data) throws IOException {
		Q.write(JCode.CONST_STRING);
		WriteVariable(ind);
		WriteString(data);
		return ind;
	}

	private int ConstObject(int ind, Object obj) throws IOException {
		if (isPrimitiveObject(obj))
			ConstPrimitiveObject(ind, obj);
		else if (obj instanceof String)
			ConstString(ind, (String) obj);
		else if (obj instanceof Variable)
			Move(((Variable) obj).Index, ind);
		return ind;
	}

	private int ConstPrimitiveObject(int ind, Object data) throws IOException {
		if (data == null) throw new IllegalArgumentException("Object can't be null");
		Class<?> c = data.getClass();
		if (c == Integer.class) {
			Q.write(JCode.CONST_INT);
			WriteVariable(ind);
			Q.write(JUtils.IntToBytes((int) data));
		} else if (c == Short.class) {
			Q.write(JCode.CONST_SHORT);
			WriteVariable(ind);
			Q.write(JUtils.ShortToBytes((short) data));
		} else if (c == Long.class) {
			Q.write(JCode.CONST_LONG);
			WriteVariable(ind);
			Q.write(JUtils.LongToBytes((long) data));
		} else if (c == Byte.class) {
			Q.write(JCode.CONST_SHORT);
			WriteVariable(ind);
			Q.write((byte) data); // 特殊的可怜人
		} else if (c == Float.class) {
			Q.write(JCode.CONST_FLOAT);
			WriteVariable(ind);
			Q.write(JUtils.FloatToBytes((float) data));
		} else if (c == Double.class) {
			Q.write(JCode.CONST_DOUBLE);
			WriteVariable(ind);
			Q.write(JUtils.DoubleToByts((double) data));
		} else if (c == Boolean.class) {
			Q.write(JCode.CONST_SHORT);
			WriteVariable(ind);
			WriteBoolean((boolean) data); // 偷偷懒！增加调用栈而已嘛~
		} else throw new IllegalArgumentException(c + " 不是一个原始类型");
		return ind;
	}

	private Object Expression() throws IOException, JLexerException, JCompilerException {
		boolean LastOp = true;
		Stack<Byte> SymbolStack = new Stack<>();
		Stack<Object> FactorStack = new Stack<>();
		Read:
		do {
			S = L.ReadSymbol();
			// TODO 特殊情况处理
			switch (S) {
				case JSymbols.Plus:
				case JSymbols.Minus:
				case JSymbols.Multiplication:
				case JSymbols.Division:
				case JSymbols.Modulo:
				case JSymbols.ShiftBitsLeft:
				case JSymbols.ShiftBitsRight:
				case JSymbols.UnsignedShiftBitsRight:
				case JSymbols.BitInversion:
				case JSymbols.BitwiseAnd:
				case JSymbols.BitwiseOr:
				case JSymbols.SelfPlus:
				case JSymbols.SelfMinus:
				case JSymbols.SelfMultiplication:
				case JSymbols.SelfDivision:
				case JSymbols.SelfModulo:
				case JSymbols.SelfShiftBitsLeft:
				case JSymbols.SelfShiftBitsRight:
				case JSymbols.SelfUnsignedShiftBitsRight:
				case JSymbols.SelfBitwiseAnd:
				case JSymbols.SelfBitwiseOr:
					if (LastOp)
						SE("连续的运算符，或者运算符处于表达式的最前面");
				case JSymbols.Semicolon:
				case JSymbols.Comma:
				case JSymbols.LeftParenthesis:
				case JSymbols.LeftSquareBracket:
				case JSymbols.RightParenthesis:
				case JSymbols.RightSquareBracket:
					LastOp = true;
					byte top;
					byte pro;
					while (!SymbolStack.empty()) {
						if ((top = SymbolStack.peek()) == JSymbols.LeftParenthesis || top == JSymbols.LeftSquareBracket)
							break;
						pro = JOperator.getPriority(top);
						if (pro <= JOperator.getPriority(S)) break;
						Object b = FactorStack.pop(), a = FactorStack.pop();
						// TODO 变量情况
						byte symbol = SymbolStack.pop();
						if ((!cx.ConstOptimize) || (a instanceof Variable) || (b instanceof Variable)) {
							int aa, bb;
							int ori = VariableTop;
							if (a instanceof Variable) aa = ((Variable) a).Index;
							else aa = ConstObject(++ori, a);
							if (b instanceof Variable) bb = ((Variable) b).Index;
							else bb = ConstObject(++ori, b);
							Q.write(JCode.OP);
							Q.write(symbol);
							WriteVariable(++VariableTop);
							WriteVariable(aa);
							WriteVariable(bb);
							FactorStack.push(new Variable(VariableTop));
							VariableTop--;
						} else {
							JOperator op = JOperator.getOperator(a);
							if (op == null)
								SE("无法找到 " + a.getClass().getName() + " " + JSymbols._GetSymbolName(top) + " " + b.getClass().getName() + " 的运算符");
							else
								FactorStack.push(op.calc(a, b, symbol));
						}
					}
					if (S == JSymbols.RightParenthesis || S == JSymbols.RightSquareBracket) {
						if (SymbolStack.empty()) break Read;
						else SymbolStack.pop();
					} else
						SymbolStack.push(S);
					if (isSeparator())
						break Read;
					break;
				default:
					if (!LastOp)
						SE("连续的元素");
					LastOp = false;
					L.gotoLast();
					FactorStack.add(Factor());
					break;
			}
		} while (true);
		return FactorStack.empty() ? null : FactorStack.pop();
	}

	private int[] Arguments = new int[256];

	private void MethodArguments(String ref, String me) throws JLexerException, JCompilerException, IOException {
		short top = 0;
		Object obj;
		do {
			obj = Expression();
			if (obj == null)
				break;
			if (isPrimitiveObject(obj))
				Arguments[++top] = ConstPrimitiveObject(VariableTop + 1, obj);
			else if (obj instanceof String)
				Arguments[++top] = ConstString(VariableTop + 1, (String) obj);
			if (S == JSymbols.RightParenthesis)
				break;
		} while (true);
		Q.write(JCode.INVOKE);
		//int res = ProcRef(ref);
		WriteShort(top);
		for (int i = 1; i <= top; i++)
			WriteVariable(Arguments[i]);
	}

	private void Move(int a, int b) throws IOException {
		Q.write(JCode.MOVE);
		WriteVariable(b);
		WriteVariable(a);
	}

	public synchronized byte[] compileCode(JLexer lexer) throws JLexerException, JCompilerException {
		this.L = lexer;
		Q = new ByteArrayOutputStream();
		JImportHelper IHelper = cx.getImportHelper();
		VariableTop = -1;
		SV = cx.isUsingShortVariableSize();
		try {
			while (true) {
				S = L.ReadSymbol();
				if (S == JSymbols.SingleLineComment || S == JSymbols.MultiLineComment) continue;
				if (S == JSymbols.EOF) break;
				if (S == 74) { // 导入(import)
					StringBuffer tmp = new StringBuffer();
					boolean pkg = false;
					boolean lastPeriod = true;
					ReadImport:
					do {
						S = L.ReadSymbol();
						switch (S) {
							case JSymbols.Identifier:
								if (!lastPeriod) SE("标识符紧连标识符");
								tmp.append(L.getToken());
								lastPeriod = false;
								break;
							case JSymbols.Period:
								if (pkg) SE("星号后连接标识符");
								if (lastPeriod) SE("连续的两个句点或句点处于第一位");
								tmp.append('.');
								lastPeriod = true;
								break;
							case JSymbols.Multiplication:
								if (!lastPeriod) SE("标识符后连接星号");
								pkg = true;
								lastPeriod = false;
								break;
							case JSymbols.Semicolon:
								if (tmp.length() == 0) SE("空白的import语句");
								if (pkg) IHelper.importPackage(tmp.substring(0, tmp.length() - 1));
								else IHelper.importClass(tmp.toString());
								tmp.setLength(0);
								tmp = null;
								break ReadImport;
							default:
								UN(S);
						}
					} while (true);
				} else if (JSymbols._IsType(S)) {
					String type;
					boolean cl = false;
					boolean callMethod = false;
					if (S == JSymbols.Identifier) {
						StringBuffer buffer = new StringBuffer(lexer.getToken());
						boolean lastPeriod = false;
						ReadType:
						do {
							S = lexer.ReadSymbol();
							switch (S) {
								case JSymbols.Period:
									if (lastPeriod) SE("Period After Period");
									lastPeriod = true;
									buffer.append('.');
									break;
								case JSymbols.Identifier:
									if (!lastPeriod) // 比如 ‘String a’
										break ReadType;
									lastPeriod = false;
									buffer.append(lexer.getToken());
									break;
								case JSymbols.LeftParenthesis: // 调用函数
									callMethod = true;
									break ReadType;
								default:
									UN(S);
							}
						} while (true);
						type = IHelper.expandClassName(buffer.toString());
						buffer.setLength(0);
						buffer = null;
						cl = true;
					} else {
						type = JSymbols._GetSymbolName(S);
						S = lexer.ReadSymbol();
					}
					if (callMethod) {
						//MethodArguments(type.);
					} else {
						if (S == JSymbols.Identifier) {
							int th;
							Variables[(th = ++VariableTop)] = lexer.getToken();
							VC[th] = getClassForType(type);
							S = lexer.ReadSymbol();
							switch (S) {
								case JSymbols.Semicolon:
									break; //忽略
								case JSymbols.Assignment: {
										ConstObject(th, Expression());
										break;
									}
								default:
									UN(S);
							}
						} else UN(S);
					}
				}
			}
			Q.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return Q.toByteArray();
	}

	public JStringPool getStringPool() {
		return P;
	}
}
